package cn.tedu._07springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 日志功能的切面类;
 * @Aspect注解: 表示当前类是一个切面类;
 */
@Aspect
@Order(value = 10)
@Component
public class LogAspect {

    /**
     * @Pointcut: 定义切入点表达式的方法;
     */
    //@Pointcut("execution(public int cn.tedu._07springaop.aop.CalculatorImpl.*(..))")
    //@Pointcut("execution(* * ..*.*(..))")
    //@Pointcut("execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..)) || execution(public int cn.tedu._07springaop.aop.CalculatorImpl.sub(..))")
    //@Pointcut("bean(calculatorImpl)")
    //@Pointcut("within(cn.tedu._07springaop.aop.CalculatorImpl)")
    @Pointcut("within(cn.tedu._07springaop.aop.*)")
    public void pointcut(){}

    /**
     * 1. @Before注解:
          1.1 前置通知;
          1.2 表示在目标方法执行之前执行;
     */
    @Before(value = "pointcut()")
    public void beforeMethod(JoinPoint joinPoint){
        //1.获取目标方法名
        String name = joinPoint.getSignature().getName();
        //2.获取目标方法参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("[前置通知]" + name + " 方法开始了,参数为:" + args);
    }

    /**
     * 2.@After注解;
     *   2.1 后置通知;
     *   2.2 在目标方法彻底结束[正常结束|异常结束]后执行;
     *   2.3 后置通知无法获取目标方法的返回值;
     */
    @After(value = "pointcut()")
    public void afterMethod(JoinPoint joinPoint){
        //1.获取目标方法名
        String name = joinPoint.getSignature().getName();
        System.out.println("[后置通知]" + name + " 方法结束了");
    }

    /**
     * 3.@AfterReturning注解:
     *   3.1 返回通知;
     *   3.2 在目标方法正常返回结束后执行,异常结束则不执行;
     *   3.3 返回通知有权限获取到目标方法的返回值;
     */
    @AfterReturning(value = "pointcut()", returning = "r")
    public void afterReturningMethod(JoinPoint joinPoint, Object r){
        String name = joinPoint.getSignature().getName();
        System.out.println("[返回通知]" + name + " 方法正常返回,结果为:" + r);
    }

    /**
     * 4.@AfterThrowing注解:
     *   4.1 异常通知;
     *   4.2 在目标方法抛出异常后执行;
     *   4.3 异常通知有权限获取到目标方法抛出的异常对象[通过throwing参数];
     */
    @AfterThrowing(value = "pointcut()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("[异常通知]" + name + " 方法抛出了异常,提示消息为:" + ex.getMessage());
    }

    /**
     * 5.@Around注解:
     *   5.1 环绕通知;
     *   5.2 等价于 @Before + @After + @AfterReturning + @AfterThrowing;
     *   5.3 环绕通知有权限获取目标方法的返回值、获取异常对象;
     *   5.4 通常通过try-catch-finally结构来处理;
     */
    @Around(value = "pointcut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        /*
            try {
                1.前置通知;
                2.执行目标方法;
                3.返回通知;
            } catch(){
                4.异常通知;
            } finally {
                5.后置通知;
            }
         */
        try {
            System.out.println("[环绕通知-前置通知]");
            result = joinPoint.proceed();
            System.out.println("[环绕通知-返回通知]");
        } catch (Throwable e) {
            System.out.println("[环绕通知-异常通知]");
        } finally {
            System.out.println("[环绕通知-后置通知]");
        }
        return result;
    }
}








